#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <netdb.h>
#include <arpa/inet.h>
#include <sys/signal.h>
#include <fcntl.h>
#include <sys/stat.h>

#define BUFFER_SIZE 1024
#define ERR_EXIT(m) \
    do { \
        perror(m); \
        exit(EXIT_FAILURE); \
    } while(0)


     /* get a string of length n-1 from stdin and clear the stdin buffer */
char* s_gets(char* stdin_buf, int n)
{
    char* ret_val;
    int i = 0;

    ret_val = fgets(stdin_buf, n, stdin);
    if(ret_val) {
        while(stdin_buf[i] != '\n' && stdin_buf[i] != '\0') {
            i++;
        }
        if(stdin_buf[i] == '\n') {
            stdin_buf[i] = '\0';
        } else {
            while (getchar() != '\n') ;
        }
    }

    return ret_val;
}

int main(void)
{
    int connect_fd, sendbytes, recvbytes, ret;
    uint16_t port_num;
    char send_buf[BUFFER_SIZE], recv_buf[BUFFER_SIZE];
    char ip_addr[INET_ADDRSTRLEN], stdin_buf[BUFFER_SIZE];
    char clr;
    struct hostent *host;
    struct sockaddr_in server_addr, connect_addr;
    socklen_t addr_len;
    pid_t childpid;
    
    printf("Input server's hostname/ipv4: "); /* www.baidu.com or an ipv4 address */
    scanf("%s", stdin_buf);
    while((clr = getchar()) != '\n' && clr != EOF); /* clear the stdin buffer */
    printf("Input server's port number: ");
    scanf("%hu", &port_num);
    while((clr = getchar()) != '\n' && clr != EOF);

    host = gethostbyname(stdin_buf);
    if(host == NULL) {
        printf("invalid name or ip address\n");
        exit(EXIT_FAILURE);
    }
    printf("server's official name = %s\n", host->h_name);
    char** ptr = host->h_addr_list;
    for(; *ptr != NULL; ptr++) {
        inet_ntop(host->h_addrtype, *ptr, ip_addr, sizeof(ip_addr));
        printf("\tserver address = %s\n", ip_addr);
    }

        /*creat connection socket*/
    connect_fd = socket(AF_INET, SOCK_STREAM, 0);
    if(connect_fd == -1) {
        ERR_EXIT("socket()");
    }

        /* set sockaddr_in of server-side */
    server_addr.sin_family = AF_INET;
    server_addr.sin_port = htons(port_num);
    server_addr.sin_addr = *((struct in_addr *)host->h_addr);
    bzero(&(server_addr.sin_zero), 8);

    addr_len = sizeof(struct sockaddr);
    ret = connect(connect_fd, (struct sockaddr *)&server_addr, addr_len); /* connect to server */
    if(ret == -1) {
        close(connect_fd);
        ERR_EXIT("connect()"); 
    }
        /* connect_fd is allocated a port_number after connecting */
    addr_len = sizeof(struct sockaddr);
    ret = getsockname(connect_fd, (struct sockaddr *)&connect_addr, &addr_len);
    if(ret == -1) {
        close(connect_fd);
        ERR_EXIT("getsockname()");
    }

    port_num = ntohs(connect_addr.sin_port);
    strcpy(ip_addr, inet_ntoa(connect_addr.sin_addr));
    printf("local socket ip addr = %s, port = %hu\n", ip_addr, port_num);

    childpid = fork();
    if(childpid < 0) {
        close(connect_fd);
        ERR_EXIT("fork()");
    }
    if(childpid > 0) { /* parent pro */
        while(1) { /* sending cycle */
            memset(send_buf, 0, BUFFER_SIZE);
            s_gets(send_buf, BUFFER_SIZE); /* without endding '\n' */
            sendbytes = send(connect_fd, send_buf, strlen(send_buf), 0); /* blocking send */
            if(sendbytes <= 0) {
                printf("sendbytes = %d. Connection terminated ...\n", sendbytes);
                break;
            }
            if(strncmp(send_buf, "end", 3) == 0)
                break;
        } 
        close(connect_fd);
        kill(childpid, SIGKILL);
    }
    else { /* child pro */
        while(1) { /* receiving cycle */
            memset(recv_buf, 0, BUFFER_SIZE);
            recvbytes = recv(connect_fd, recv_buf, BUFFER_SIZE, 0); /* blocking receive */
            if(recvbytes <= 0) {
                printf("recvbytes = %d. Connection terminated ...\n", recvbytes);
                break;
            }
            printf("\t\t\t\t\tserver %s >>>> %s\n", ip_addr, recv_buf);
            if(strncmp(recv_buf, "end", 3) == 0)
                break;
        }
        close(connect_fd);
        kill(getppid(), SIGKILL);
    }
    return EXIT_SUCCESS;
}

